#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <assert.h>
#include "config.h"
#include "strlist.h"
#include "strings.h"
#include "file.h"


Config::Config(const char *cfg_file){
    if(!file_exists(cfg_file)){
        printf("Config file %s is not exists..\n", cfg_file);
        exit(-1);
    }
    this->is_daemon = false;
    this->is_debug = false;
    this->load(cfg_file); 
    int ret = socketpair( PF_UNIX, SOCK_STREAM, 0, this->pipefd );
    assert(ret == 0); 
}

inline static
char *get_config_key(char *cfg_line){
    char *p;    
    if(is_empty_str(cfg_line)){
        p = NULL;
        return p;
    }
    int size = strlen(cfg_line);
    int c = 0;
    p = (char *)malloc(size + 1);
    memset(p, '\0', size + 1);
    memcpy(p, cfg_line, size);
        
    while( *p != '=' ){
        p++;     
        c++;
    }
    *(p) = '\0';
    p -= c;    
    return trim(p);
}

static char *parse_config_line(char *cfg_line){    
    char *p = NULL;
    if(is_empty_str(cfg_line)){        
        return p;
    }
    
    p = strpbrk(cfg_line, "=");
    p++;
    if( is_empty_str(p) ){
        p = NULL;
        return p;
    }
    
    return trim(p);    
}


void Config::load(const char *cfg_file){         
    FILE *fp = NULL;
    int lineno = 0;
    fp = fopen(cfg_file, "r");
    if(!fp){
        printf("Open config file [%s] failure..\n", cfg_file);
        exit(-1);
    }
    
    char buf[CONFIG_MAX_LINE]; 
    this->allow_ip_list = create_strlink();
    this->deny_ip_list = create_strlink();
    
    while( fgets(buf, sizeof(buf), fp) ){
        if( lineno > CONFIG_MAX_LINE){
            printf("config file line is over the max limits.\n"); //超过限制就退出解析
            break;
        }
        lineno++;
        buf[strlen(buf) - 1] = '\0'; /* 替换 \n */
        if(is_empty_str(buf)){
            continue;
        }
        
        int indent = strspn(buf, " \t");
        char *key = buf + indent;
        
        if( *key == '#' ){
            continue;
        }
                      
        //printf("key: %s\n", key);        
        
        if( strcmp(get_config_key(key), "sync_path") == 0 ){
            this->set_sync_path(key);              
        }else if( strcmp(get_config_key(key), "debug") == 0 ){            
            this->set_debug(key);
        }else if( strcmp(get_config_key(key), "pidfile") == 0 ){            
            this->set_pidfile(key);
        }else if( strcmp(get_config_key(key), "user") == 0 ){            
            this->set_user(key);
        }else if( strcmp(get_config_key(key), "group") == 0 ){            
            this->set_group(key);
        }else if( strcmp(get_config_key(key), "server_ip") == 0 ){            
            this->set_server_ip(key);
        }else if( strcmp(get_config_key(key), "server_port") == 0 ){            
            this->set_server_port(key);
        }else if( strcmp(get_config_key(key), "allow_ip") == 0 ){            
            this->set_allow_ip(key);
        }else if( strcmp(get_config_key(key), "deny_ip") == 0 ){            
            this->set_deny_ip(key);
        }else if( strcmp(get_config_key(key), "slave_ip") == 0 ){            
            this->set_slave_ip(key);
        }
        
    }
        
}

void Config::set_sync_path(char *cfg_line){    
    this->sync_path = parse_config_line(cfg_line);                
        
}

void Config::set_debug(char *cfg_line){
    this->is_debug = atoi(parse_config_line(cfg_line)) > 0;  
}

void Config::set_pidfile(char *cfg_line){
    this->pidfile = parse_config_line(cfg_line);  
}

void Config::set_user(char *cfg_line){
    this->user = parse_config_line(cfg_line);  
}

void Config::set_group(char *cfg_line){
    this->group = parse_config_line(cfg_line);  
}

void Config::set_server_ip(char *cfg_line){
    char *host = parse_config_line(cfg_line);
    if( is_ip(host) ){
        this->server_ip = host;
    }    
}

void Config::set_server_port(char *cfg_line){
    this->server_port = atoi(parse_config_line(cfg_line));
    if( (this->server_port < 1000) || (this->server_port > 65535) ){       
        this->server_port = 9999;
    }
}

void Config::set_allow_ip(char *cfg_line){
    if( strlen(cfg_line) > 0 ){        
        link_entry_t ip;
        ip = (link_entry_t)malloc( sizeof(struct link_entry) );        
        ip->str = (char *)malloc(sizeof(char));
        strcpy(ip->str, parse_config_line(cfg_line));
        if( is_ip(ip->str) ){
            insert_strlink(ip, this->allow_ip_list);
            //printf("allow ip: %s  len: %d\n", this->allow_ip_list->head->str, this->allow_ip_list->len);
            //printf("find the same:%s count:%d\n", this->allow_ip_list->tail->str, find_strlink(this->allow_ip_list->tail->str, this->allow_ip_list));
        }
    }
}

void Config::set_deny_ip(char *cfg_line){
    if( strlen(cfg_line) > 0 ){        
        link_entry_t ip;
        ip = (link_entry_t)malloc( sizeof(struct link_entry) );        
        ip->str = (char *)malloc(sizeof(char));
        strcpy(ip->str, parse_config_line(cfg_line));
        if( is_ip(ip->str) ){
            insert_strlink(ip, this->deny_ip_list);
            //printf("deny ip: %s  len: %d\n", this->deny_ip_list->head->str, this->deny_ip_list->len);
        }
    }
}

void Config::set_slave_ip(char *cfg_line){   
    std::string ip;
    int port;    
    if( strlen(cfg_line) > 0 ){
        char *ptr = parse_config_line(cfg_line);        
        char *p = strpbrk(ptr, ":");
        if( NULL == p ) return;
        *(p++) = '\0';
        ip = ptr;
        //printf("ip:%s\n", ip.c_str());
        if( !is_ip(ip.c_str()) ) return;
        port = atoi(trim(p));
        //printf("port:%d\n", port);
        if(port < 0 || port > 65535) return;        
        this->slave_ip.insert( std::map<string, int>::value_type(ip, port) );
    }
}

Config::~Config(){
    free(allow_ip_list);
    free(deny_ip_list);
}

void test(){
    char tmp[100];
    memset(tmp, '\0', sizeof(tmp));
    snprintf(tmp, sizeof(tmp) - 1, "%s", " ABCD");
    const char *str = " ABCDEFG ";
    std::string a = str;
    strtolower(&a);
    printf("tmp:[%s], trim->tmp:[%s], strtolower->str:[%s]\n", tmp, trim(tmp), a.c_str());        
}